package business;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import util.Globals;
import exception.FileComponentIsNullException;
import exception.NoCodeFileFoundException;
import exception.NoOthersPartsOfApplicationOnFileImportedException;


public class NetLogoFile {
	private FileComponent fileRead;
	private FileComponent fileToGenerate;
	private FileIOManipulate fileManipulate;

	public NetLogoFile(String fileReadLocalization, String fileToGenerateLocalization) throws IOException{
		this.fileRead = new FileComponent(fileReadLocalization);
		this.fileToGenerate = new FileComponent(fileToGenerateLocalization);
		this.fileManipulate = new FileIOManipulate(fileReadLocalization, fileToGenerateLocalization);
	}

	/**
	 * Reads the imported file specified.
	 * After, sets the file generated with the same contents.
	 * 
	 * @throws IOException
	 */
	public void readImportedFile() throws IOException{
		/* Reads imported file. */
		fileManipulate.readImportedFile(fileRead);

		/* Populate the generated file with the same contents. */
		fileToGenerate.setNetlogoCode(fileRead.getNetlogoCode());
		fileToGenerate.setNetlogoOthersPartsOfApplicantion(fileRead.getNetlogoOthersPartsOfApplicantion());
	}

	/**
	 * Prints the netlogo code from imported file.
	 * 
	 * @throws IOException
	 * @throws FileComponentIsNullException
	 */
	public void printImportedCodeFile() throws IOException, FileComponentIsNullException{
		printFileCode(fileRead);
	}

	/**
	 * Prints the netlogo code from the joined file (aspect code + original netlogo code).
	 * 
	 * @throws IOException
	 * @throws FileComponentIsNullException
	 */
	public void printGeneratedCodeFile() throws IOException, FileComponentIsNullException{
		printFileCode(fileToGenerate);
	}

	/**
	 * Prints the netlogo code and the others parts of the application from imported file.
	 * 
	 * @throws IOException
	 * @throws FileComponentIsNullException
	 */
	public void printImportedCompleteFile() throws IOException, FileComponentIsNullException{
		printCompleteFile(fileRead);
	}

	/**
	 * Prints the netlogo code and the others parts of the application from the joined file 
	 * (aspect code + original netlogo code).
	 * 
	 * @throws IOException
	 * @throws FileComponentIsNullException
	 */
	public void printGeneratedCompleteFile() throws IOException, FileComponentIsNullException{
		printCompleteFile(fileToGenerate);
	}


	/**
	 * Receive a file component (read file or generated file) and prints the code.
	 * 
	 * @param fileComponent
	 * @throws IOException
	 * @throws FileComponentIsNullException
	 */
	private void printFileCode(FileComponent fileComponent) throws IOException, FileComponentIsNullException{
		if(fileRead == null)
			throw new FileComponentIsNullException();

		try{
			fileManipulate.printCodeFile(fileComponent);
		}catch(NoCodeFileFoundException e){
			e.getMessage();
		}
	}

	/**
	 * Receive a file component (read file or generated file) and prints all inside (code + other parts of application).
	 * 
	 * @param fileComponent
	 * @throws IOException
	 * @throws FileComponentIsNullException
	 */
	private void printCompleteFile(FileComponent fileComponent) throws IOException, FileComponentIsNullException{
		if(fileComponent == null)
			throw new FileComponentIsNullException();

		try{
			fileManipulate.printCompleteFile(fileComponent);
		}catch(NoCodeFileFoundException e){
			e.getMessage();
		}catch(NoOthersPartsOfApplicationOnFileImportedException e){
			e.getMessage();
		}
	}

	/**
	 * Adds code in last position of the code received by parameter.
	 * This method is used to put a intertype inside in the code.
	 * 
	 * @param codeToAdd
	 * @param netlogoCode
	 */
	public void addCodeInLastPosition(String codeToAdd, NetlogoCode netlogoCode){
		/* Get the last position code. */
		int codeToAddPosition = netlogoCode.getCodeLines().get(netlogoCode.getCodeLines().size()-1).getPosition();
		List<String> codeInLines = Globals.splitWithBreakLine(codeToAdd);

		/* Break line before put the code. */
		netlogoCode.getCodeLines().add(new Line(++codeToAddPosition, String.valueOf(Globals.SPACE), Globals.FALSE));

		/* For each line of the code received, puts it on the end file. */
		for(String codeLine : codeInLines){
			boolean codeToAddHasComment = Globals.hasCommentInLine(codeLine);

			Line newCodeLine = new Line(++codeToAddPosition, codeLine, codeToAddHasComment);
			netlogoCode.getCodeLines().add(newCodeLine);
		}
	}

	/**
	 * Removes the old function and puts the new received by parameter.
	 * 
	 * @param codePosition
	 * @param codeToInclude
	 */
	public void replaceOldCode(CodePosition codePosition, String codeToInclude){
		int positionToInclude = 0;

		/* The new netlogo will be add in this list. */
		List<Line> newNetLogoCodeList = new ArrayList<Line>();

		/* Gets the code before the function that will be replaced. */
		List<Line> linesBeforeCode = fileToGenerate.getNetlogoCode().getCodeLines().subList
				(Globals.ZERO, codePosition.getBeginLineCode().getPosition());

		/* Gets the code after the function that will be replaced. */
		List<Line> linesAfterCode = fileToGenerate.getNetlogoCode().getCodeLines().subList
				(codePosition.getEndLineCode().getPosition() + Globals.ONE, fileToGenerate.getNetlogoCode().getCodeLines().size());

		/* Fill the new netlogo code with the functions before the old function. */
		for(Line line : linesBeforeCode){
			newNetLogoCodeList.add(line);
		}

		/* Fill the new netlogo code with the new code. */
		positionToInclude = newNetLogoCodeList.size();
		List<String> codeInLines = Globals.splitWithBreakLine(codeToInclude);
		for(String codeLine : codeInLines){
			boolean codeToAddHasComment = Globals.hasCommentInLine(codeLine);

			Line newCodeLine = new Line(positionToInclude++, codeLine, codeToAddHasComment);
			newNetLogoCodeList.add(newCodeLine);
		}

		/* Fill the new netlogo code with the functions after the old function. */
		positionToInclude = newNetLogoCodeList.size();
		for(Line line : linesAfterCode){
			line.setPosition(positionToInclude++);
			newNetLogoCodeList.add(line);
		}

		/* Sets the new code on file that will be generated. */
		fileToGenerate.setNetlogoCode(new NetlogoCode(newNetLogoCodeList));
	}

	/** 
	 * Adds a new code on line (param) position (param) received.
	 * 
	 * @param newCode
	 * @param netlogoCodeLines
	 * @param functionPosition
	 */
	public void addVarDeclarationOnFunction(String newCode, String varBlock, List<Line> netlogoCodeLines, CodePosition functionPosition, boolean isFunctionScope){
		int varLine = functionPosition.getBeginLineCode().getPosition();
		int posInLine = functionPosition.getPositionInLineStartsDeclarationCode();
		
		String line = netlogoCodeLines.get(varLine).getValue();
		
		String lineBeforeId = "";
		if(posInLine != Globals.ZERO)
			lineBeforeId = line.substring(Globals.ZERO, posInLine);
		else
			lineBeforeId = line.substring(Globals.ZERO);
		
		String lineAfterId = "";
		if(line.substring(posInLine, line.length()).length() > varBlock.length()){
			lineAfterId = line.substring(posInLine + varBlock.length());
		}
		
		netlogoCodeLines.get(varLine).setValue(lineBeforeId + String.valueOf(Globals.SPACE) + (isFunctionScope ? varBlock : Globals.L_BRACKET));
		netlogoCodeLines.add(varLine + Globals.ONE, new Line(varLine + Globals.ONE, Globals.DOUBLE_SPACE + newCode + lineAfterId, netlogoCodeLines.get(varLine).isHasComment()));
	}

	/**
	 * Atts the line positions  of code after alterations.
	 * 
	 * @param netlogoCode
	 */
	public void setCorrectPositionOnCode(NetlogoCode netlogoCode){
		int position = 0;
		for(Line line : netlogoCode.getCodeLines()){
			line.setPosition(position++);
		}
	}

	/**
	 * Atts the function declaration list after alterations.
	 * 
	 * @param netlogoCode
	 */
	public void setCorrectFunctionListNames(NetlogoCode netlogoCode){
		List<Line> declaratedFunctionList = new ArrayList<Line>();

		for(Line actualLine : netlogoCode.getCodeLines()){
			boolean lineHasCommentWord = false;

			List<String> lineSplitArrayList = Globals.splitWithSpace(actualLine);
			for(String word : lineSplitArrayList){
				if(!lineHasCommentWord)
					lineHasCommentWord = word.equalsIgnoreCase(String.valueOf(Globals.SEMICOLON)) || word.equalsIgnoreCase(Globals.DOUBLE_SEMICOLON);

				if(lineHasCommentWord)
					break;

				/* Puts the function on the function declaration list. */
				if(word.equalsIgnoreCase(Globals.FUNCTION_DECLARATION_NO_RETURN) ||
						word.equalsIgnoreCase(Globals.FUNCTION_DECLARATION_RETURN)){
					declaratedFunctionList.add(new Line(actualLine.getPosition(), actualLine.getValue(), actualLine.isHasComment()));
				}
			}
		}

		/* Att the declaration function list. */
		netlogoCode.setNetlogoLinesFunctionNames(declaratedFunctionList);
	}

	public FileComponent getFileRead() {
		return fileRead;
	}

	public void setFileRead(FileComponent fileRead) {
		this.fileRead = fileRead;
	}

	public FileComponent getFileToGenerate() {
		return fileToGenerate;
	}

	public void setFileToGenerate(FileComponent fileToGenerate) {
		this.fileToGenerate = fileToGenerate;
	}

	public FileIOManipulate getFileManipulate() {
		return fileManipulate;
	}

	public void setFileManipulate(FileIOManipulate fileManipulate) {
		this.fileManipulate = fileManipulate;
	}

}
